package com.alander.nexusgo.user.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alander.nexusgo.common.model.dto.RegisterDTO;
import com.alander.nexusgo.common.model.dto.UserListDTO;
import com.alander.nexusgo.common.model.vo.UserInfoVO;
import com.alander.nexusgo.framework.file.starter.service.FileUploadService;
import com.alander.nexusgo.framework.redis.starter.key.UserCacheKeyBuilder;
import com.alander.nexusgo.user.mapper.UserMapper;
import com.alander.nexusgo.user.mapper.UserSocialMapper;
import com.alander.nexusgo.user.model.entity.User;
import com.alander.nexusgo.user.model.entity.UserSocial;
import com.alander.nexusgo.user.service.UserService;
import com.alander.nexusgo.user.utils.PasswordEncoderUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author alander
 * @createDate 2025-10-07 17:08:55
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    private final UserSocialMapper userSocialMapper;

    private final FileUploadService fileUploadService;

    private final RedisTemplate<String, Object> redisTemplate;

    private final UserCacheKeyBuilder userCacheKeyBuilder;

    @Value("${oauth2.github.clientId}")
    private String githubClientId;

    @Value("${oauth2.github.redirectUri}")
    private String githubRedirectUri;

    @Value("${oauth2.github.clientSecret}")
    private String githubClientSecret;

    @Value("${oauth2.wechat.clientId}")
    private String wechatClientId;

    @Value("${oauth2.wechat.redirectUri}")
    private String wechatRedirectUri;

    @Value("${oauth2.wechat.clientSecret}")
    private String wechatClientSecret;

    @Value("${oauth2.qq.clientId}")
    private String qqClientId;

    @Value("${oauth2.qq.redirectUri}")
    private String qqRedirectUri;

    @Value("${oauth2.qq.clientSecret}")
    private String qqClientSecret;

    public User getUser(Long userId) {
        String userCacheKey = userCacheKeyBuilder.buildUserInfoKey(String.valueOf(userId));

        User user = (User) redisTemplate.opsForValue().get(userCacheKey);
        if (user == null) {
            user = this.getById(userId);
            redisTemplate.opsForValue().set(userCacheKey, user, 30, TimeUnit.MINUTES);
        }
        return user;
    }

    public User getUser(String userName) {
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getUsername, userName);
        return this.getOne(userLambdaQueryWrapper);
    }

    @Override
    public String login(String username, String password, String clientIp) {
        if (StrUtil.isBlank(username) || StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("用户名或密码不能为空");
        }

        User user = getUser(username);
        if (user == null) {
            log.warn("用户登录失败，用户不存在: {}", username);
            throw new RuntimeException("用户不存在");
        }

        if (PasswordEncoderUtil.matches(password, user.getPassword())) {
            try {
                StpUtil.login(user.getId());
                LocalDateTime loginTime = LocalDateTime.now();
                user.setLastLoginTime(loginTime);
                user.setLastLoginIp(clientIp);
                this.updateById(user);

                String userCacheKey = userCacheKeyBuilder.buildUserInfoKey(String.valueOf(user.getId()));
                redisTemplate.delete(userCacheKey);
                log.info("用户登录成功: {}", username);
            } catch (Exception e) {
                log.error("登录后处理失败，用户ID: {}", user.getId(), e);
                throw new RuntimeException("登录处理失败");
            }
        } else {
            log.warn("用户登录失败，密码错误: {}", username);
            throw new RuntimeException("用户名或密码错误");
        }
        return StpUtil.getTokenValue();
    }

    @Override
    public String getOAuth2RedirectUrl(String provider) {
        // 根据不同的第三方平台生成对应的授权URL
        return switch (provider.toLowerCase()) {
            case "github" ->
                    "https://github.com/login/oauth/authorize?" + "client_id=" + githubClientId + "&redirect_uri=" + githubRedirectUri + "&scope=read:user user:email";
            case "qq" ->
                    "https://graph.qq.com/oauth2.0/authorize?" + "response_type=code" + "&client_id=" + qqClientId + "&redirect_uri=" + qqRedirectUri + "&scope=get_user_info";
            case "wechat" ->
                    "https://open.weixin.qq.com/connect/qrconnect?" + "appid=" + wechatClientId + "&redirect_uri=" + wechatRedirectUri + "&response_type=code" + "&scope=snsapi_login" + "&state=STATE#wechat_redirect";
            default -> throw new IllegalArgumentException("不支持的第三方平台: " + provider);
        };
    }
    
    @Override
    public Map<String, Object> handleOAuth2Callback(String provider, String code, String state, String clientIp) throws Exception {
        Map<String, Object> userInfo = switch (provider.toLowerCase()) {
            case "github" -> handleGithubCallback(code);
            case "qq" -> handleQQCallback(code);
            case "wechat" -> handleWechatCallback(code);
            default -> throw new IllegalArgumentException("不支持的第三方平台: " + provider);
        };

        // 根据不同平台获取用户信息
        if (userInfo.isEmpty()) {
            throw new RuntimeException("获取第三方用户信息失败");
        }

        // 获取第三方平台的openId
        String openId = (String) userInfo.get("openId");
        if (StrUtil.isBlank(openId)) {
            throw new RuntimeException("获取第三方用户标识失败");
        }

        // 查询是否已绑定该第三方账号
        LambdaQueryWrapper<UserSocial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserSocial::getSource, provider)
                .eq(UserSocial::getOpenId, openId);
        UserSocial userSocial = userSocialMapper.selectOne(queryWrapper);

        User user = null;
        if (userSocial != null) {
            // 已绑定，直接登录
            user = this.getById(userSocial.getUserId());
            if (user == null || "1".equals(user.getDelFlag())) {
                throw new RuntimeException("用户不存在或已被删除");
            }
            // 更新第三方用户信息
            updateSocialUserInfo(userSocial, userInfo, code, clientIp);
        } else {
            // 未绑定，创建新用户或绑定到现有用户
            user = createOrBindUser(provider, openId, userInfo, code, clientIp);
        }

        // 使用Sa-Token登录
        StpUtil.login(user.getId());

        // 构建返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("token", StpUtil.getTokenValue());

        return result;
    }

    @Override
    public boolean bindSocialAccount(Long userId, String provider, String openId, String unionId,
                                     String accessToken, String refreshToken, LocalDate expireTime,
                                     Map<String, Object> userInfo) {
        try {
            // 检查是否已绑定
            LambdaQueryWrapper<UserSocial> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserSocial::getUserId, userId)
                    .eq(UserSocial::getSource, provider);
            if (userSocialMapper.selectCount(queryWrapper) > 0) {
                throw new RuntimeException("该第三方账号已绑定");
            }

            // 检查该第三方账号是否已被其他用户绑定
            queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserSocial::getSource, provider)
                    .eq(UserSocial::getOpenId, openId);
            if (userSocialMapper.selectCount(queryWrapper) > 0) {
                throw new RuntimeException("该第三方账号已被其他用户绑定");
            }

            // 创建绑定记录
            UserSocial userSocial = new UserSocial();
            userSocial.setUserId(userId);
            userSocial.setSource(provider);
            userSocial.setOpenId(openId);
            userSocial.setUnionId(unionId);
            userSocial.setAccessToken(accessToken);
            userSocial.setRefreshToken(refreshToken);
            userSocial.setExpireTime(expireTime.atStartOfDay());
            userSocial.setUserInfo(JSONUtil.toJsonStr(userInfo));
            userSocial.setStatus("0");
            userSocial.setDelFlag("0");

            return userSocialMapper.insert(userSocial) > 0;
        } catch (Exception e) {
            log.error("绑定第三方账号失败，用户ID: {}, 平台: {}", userId, provider, e);
            throw new RuntimeException("绑定失败: " + e.getMessage());
        }
    }

    @Override
    public boolean unbindSocialAccount(Long userId, String provider) {
        try {
            LambdaQueryWrapper<UserSocial> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserSocial::getUserId, userId)
                    .eq(UserSocial::getSource, provider);

            UserSocial userSocial = userSocialMapper.selectOne(queryWrapper);
            if (userSocial == null) {
                throw new RuntimeException("未找到绑定的第三方账号");
            }

            return userSocialMapper.deleteById(userSocial.getId()) > 0;
        } catch (Exception e) {
            log.error("解绑第三方账号失败，用户ID: {}, 平台: {}", userId, provider, e);
            throw new RuntimeException("解绑失败: " + e.getMessage());
        }
    }

    // 辅助方法
    private User createOrBindUser(String provider, String openId, Map<String, Object> socialUserInfo, String code, String clientIp) {
        // 创建新用户
        User user = new User();
        user.setUsername((String) socialUserInfo.getOrDefault("username", provider + "_user"));
        user.setNickname((String) socialUserInfo.getOrDefault("nickname", provider + "_user"));
        user.setAvatar((String) socialUserInfo.getOrDefault("avatar", ""));
        user.setGender((String) socialUserInfo.getOrDefault("gender", 0));
        user.setBirthday((LocalDate) socialUserInfo.getOrDefault("birthday", null));
        user.setLastLoginIp(clientIp);
        user.setLastLoginTime(LocalDateTime.now());
        user.setPassword("");
        user.setStatus("0");
        user.setDelFlag("0");

        this.save(user);

        // 绑定第三方账号
        UserSocial userSocial = new UserSocial();
        userSocial.setUserId(user.getId());
        userSocial.setSource(provider);
        userSocial.setOpenId(openId);
        userSocial.setExpireTime(LocalDateTime.now().plusDays(30));
        userSocial.setUserInfo(JSONUtil.toJsonStr(socialUserInfo));
        userSocial.setStatus("0");
        userSocial.setDelFlag("0");

        userSocialMapper.insert(userSocial);

        return user;
    }

    private void updateSocialUserInfo(UserSocial userSocial, Map<String, Object> socialUserInfo, String code, String clientIp) {
        userSocial.setUserInfo(JSONUtil.toJsonStr(socialUserInfo));
        userSocial.setUpdateTime(LocalDateTime.now());
        userSocialMapper.updateById(userSocial);

        User user = getById(userSocial.getUserId());
        user.setLastLoginIp(clientIp);
        user.setLastLoginTime(LocalDateTime.now());
        this.updateById(user);
    }

    @Override
    public UserInfoVO getUserInfo(String loginId) {
        User user = getUser(Long.valueOf(loginId));
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtil.copyProperties(user, userInfoVO);
        return userInfoVO;
    }

    @Override
    public UserInfoVO getUserInfoByName(String username) {
        User user = getUser(username);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtil.copyProperties(user, userInfoVO);

        return userInfoVO;
    }

    @Override
    public boolean register(RegisterDTO registerDTO) {
        if (hasName(registerDTO.getUsername())) {
            throw new RuntimeException("用户名已存在");
        }
        User newUser = new User();
        BeanUtil.copyProperties(registerDTO, newUser);
        newUser.setPassword(PasswordEncoderUtil.encode(newUser.getPassword()));
        newUser.setNickname(registerDTO.getUsername());
        newUser.setPwdUpdateTime(LocalDateTime.now());
        try {
            this.save(newUser);

            String userCacheKey = userCacheKeyBuilder.buildUserInfoKey(String.valueOf(newUser.getId()));
            redisTemplate.opsForValue().set(userCacheKey, newUser);
        } catch (Exception e) {
            log.error("用户注册失败，用户名: {}", registerDTO.getUsername(), e);
            throw new RuntimeException("注册失败");
        }
        return true;
    }

    @Override
    public boolean hasName(String userName) {
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getUsername, userName);
        return this.count(userLambdaQueryWrapper) > 0;
    }

    @Override
    public Boolean updateUserInfo(UserInfoVO userInfoVO) {
        User user = this.getUser(userInfoVO.getId());
        CopyOptions copyOptions = CopyOptions.create()
                .setIgnoreNullValue(true)
                .setIgnoreError(true)
                .setFieldValueEditor((key, value) -> {
                    // 如果是空字符串，返回 null
                    if (value instanceof String && ((String) value).isEmpty()) {
                        return null;
                    }
                    return value;
                });

        BeanUtil.copyProperties(userInfoVO, user, copyOptions);

        // 更新数据库
        boolean result = this.updateById(user);

        // 清除相关缓存
        if (result) {
            String userCacheKey = userCacheKeyBuilder.buildUserInfoKey(String.valueOf(user.getId()));
            redisTemplate.delete(userCacheKey);
        }

        return result;
    }

    @Override
    public boolean logout() {
        if (!StpUtil.isLogin()) {
            return true;
        }
        StpUtil.logout();
        return true;
    }

    @Override
    public String refreshToken() {
        // 检查当前是否已登录
        if (!StpUtil.isLogin()) {
            throw new RuntimeException("用户未登录");
        }
        // 获取当前登录用户的ID
        Object loginId = StpUtil.getLoginId();
        // 注销当前会话
        StpUtil.logout();
        // 创建新的会话
        StpUtil.login(loginId);
        // 返回新的token
        return StpUtil.getTokenValue();
    }

    @Override
    public List<UserInfoVO> listUserInfo(UserListDTO userListDTO) {
        try {
            LambdaQueryWrapper<User> listLambdaQueryWrapper = new LambdaQueryWrapper<>();
            listLambdaQueryWrapper
                    .eq(userListDTO.getId() != null, User::getId, userListDTO.getId())
                    .eq(StrUtil.isNotBlank(userListDTO.getStatus()), User::getStatus, userListDTO.getStatus())
                    .eq(StrUtil.isNotBlank(userListDTO.getDelFlag()), User::getDelFlag, userListDTO.getDelFlag())
                    .eq(StrUtil.isNotBlank(userListDTO.getGender()), User::getGender, userListDTO.getGender())
                    .like(StrUtil.isNotBlank(userListDTO.getUsername()), User::getUsername, userListDTO.getUsername())
                    .like(StrUtil.isNotBlank(userListDTO.getNickname()), User::getNickname, userListDTO.getNickname())
                    .like(StrUtil.isNotBlank(userListDTO.getEmail()), User::getEmail, userListDTO.getEmail())
                    .like(StrUtil.isNotBlank(userListDTO.getPhone()), User::getPhone, userListDTO.getPhone())
                    // 时间范围查询
                    .ge(userListDTO.getStartTime() != null, User::getCreateTime, userListDTO.getStartTime())
                    .le(userListDTO.getEndTime() != null, User::getCreateTime, userListDTO.getEndTime());

            List<User> userList = this.list(listLambdaQueryWrapper);
            return BeanUtil.copyToList(userList, UserInfoVO.class);
        } catch (Exception e) {
            log.error("查询用户列表失败，查询条件: {}", userListDTO, e);
            throw new RuntimeException("查询用户列表失败", e);
        }
    }

    @Override
    public Boolean updatePassword(String oldPassword, String newPassword) {
        if (StrUtil.isBlank(oldPassword) || StrUtil.isBlank(newPassword)) {
            throw new IllegalArgumentException("密码不能为空");
        }

        try {
            User user = getUser((Long) StpUtil.getLoginId());
            if (user == null) {
                throw new RuntimeException("用户不存在");
            }

            user.setPwdUpdateTime(LocalDateTime.now());
            if (PasswordEncoderUtil.matches(oldPassword, user.getPassword())) {
                user.setPassword(PasswordEncoderUtil.encode(newPassword));
                return this.updateById(user);
            } else {
                throw new RuntimeException("原密码错误");
            }
        } catch (Exception e) {
            log.error("更新密码失败，用户ID: {}", StpUtil.getLoginId(), e);
            throw new RuntimeException("密码更新失败");
        }
    }


    @Override
    public Boolean save(UserInfoVO userInfoVO) {
        User user = new User();
        BeanUtil.copyProperties(userInfoVO, user);
        return this.save(user);
    }

    @Override
    public Boolean deleteById(Long userId) {
        User user = new User();
        user.setId(userId);
        user.setDelFlag("1");
        return this.updateById(user);
    }

    @Override
    public Boolean updateStatus(Long userId, Integer status) {
        User user = new User();
        user.setId(userId);
        user.setStatus(String.valueOf(status));
        return this.updateById(user);
    }

    @Override
    public String updateAvatar(MultipartFile avatar) {
        String avatarUrl = null;
        try {
            avatarUrl = fileUploadService.uploadFile(avatar);
        }catch (IOException e){
            log.error("上传文件失败", e);
        }
        User user = this.getUser(StpUtil.getLoginIdAsLong());
        user.setAvatar(avatarUrl);
        boolean update = this.updateById(user);
        if (update) {
            String userCacheKey = userCacheKeyBuilder.buildUserInfoKey(String.valueOf(user.getId()));
            redisTemplate.delete(userCacheKey);
        }
        return avatarUrl;
    }

    private Map<String, Object> handleGithubCallback(String code) {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();

        try {
            // 1. 获取 access_token
            FormBody tokenBody = new FormBody.Builder()
                    .add("client_id", githubClientId)
                    .add("client_secret", githubClientSecret)
                    .add("code", code)
                    .add("redirect_uri", githubRedirectUri)
                    .build();

            Request tokenRequest = new Request.Builder()
                    .url("https://github.com/login/oauth/access_token")
                    .post(tokenBody)
                    .addHeader("Accept", "application/json")
                    .build();

            String tokenResponse = client.newCall(tokenRequest).execute().body().string();
            String accessToken = parseAccessToken(tokenResponse);

            // 2. 获取用户信息
            Request userRequest = new Request.Builder()
                    .url("https://api.github.com/user")
                    .addHeader("Authorization", "Bearer " + accessToken)
                    .addHeader("User-Agent", "NexusGo")
                    .build();

            String userResponse = client.newCall(userRequest).execute().body().string();
            JSONObject userInfo = JSONUtil.parseObj(userResponse);

            Map<String, Object> result = new HashMap<>();
            result.put("openId", userInfo.getStr("id"));
            result.put("username", userInfo.getStr("login"));
            result.put("nickname", userInfo.getStr("login"));
            result.put("avatar_url", userInfo.getStr("avatar_url"));
            result.put("email", userInfo.getStr("email"));
            result.put("avatar", userInfo.getStr("avatar_url"));
            result.put("accessToken", accessToken);

            return result;
        } catch (Exception e) {
            log.error("GitHub 认证失败", e);
            throw new RuntimeException("GitHub 认证失败");
        }
    }

    private Map<String, Object> handleQQCallback(String code) {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();

        try {
            // 1. 获取 access_token
            String tokenUrl = "https://graph.qq.com/oauth2.0/token";
            HttpUrl.Builder urlBuilder = HttpUrl.parse(tokenUrl).newBuilder();
            urlBuilder.addQueryParameter("client_id", qqClientId);
            urlBuilder.addQueryParameter("client_secret", qqClientSecret);
            urlBuilder.addQueryParameter("code", code);
            urlBuilder.addQueryParameter("redirect_uri", qqRedirectUri);
            urlBuilder.addQueryParameter("grant_type", "authorization_code");

            Request tokenRequest = new Request.Builder()
                    .url(urlBuilder.build())
                    .get()
                    .addHeader("User-Agent", "NexusGo")
                    .build();

            String tokenResponse = client.newCall(tokenRequest).execute().body().string();

            // 解析 access_token
            String accessToken = parseQQAccessToken(tokenResponse);

            // 2. 获取 openid
            String openidUrl = "https://graph.qq.com/oauth2.0/me";
            HttpUrl.Builder openidUrlBuilder = HttpUrl.parse(openidUrl).newBuilder();
            openidUrlBuilder.addQueryParameter("access_token", accessToken);

            Request openidRequest = new Request.Builder()
                    .url(openidUrlBuilder.build())
                    .get()
                    .addHeader("User-Agent", "NexusGo")
                    .build();

            String openidResponse = client.newCall(openidRequest).execute().body().string();
            String openId = parseQQOpenId(openidResponse);

            // 3. 使用 access_token 和 openid 获取用户信息
            String userUrl = "https://graph.qq.com/user/get_user_info";
            HttpUrl.Builder userUrlBuilder = HttpUrl.parse(userUrl).newBuilder();
            userUrlBuilder.addQueryParameter("access_token", accessToken);
            userUrlBuilder.addQueryParameter("oauth_consumer_key", qqClientId);
            userUrlBuilder.addQueryParameter("openid", openId);

            Request userRequest = new Request.Builder()
                    .url(userUrlBuilder.build())
                    .get()
                    .addHeader("User-Agent", "NexusGo")
                    .build();

            String userResponse = client.newCall(userRequest).execute().body().string();
            JSONObject userInfo = JSONUtil.parseObj(userResponse);

            // 构造返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("openId", openId);
            result.put("username", userInfo.getStr("nickname"));
            result.put("nickname", userInfo.getStr("nickname"));
            result.put("avatar", userInfo.getStr("figureurl_qq_1"));
            result.put("gender",userInfo.getStr("gender"));
            result.put("accessToken", accessToken);

            return result;
        } catch (Exception e) {
            log.error("处理 QQ 回调失败，code: {}", code, e);
            throw new RuntimeException("处理 QQ 回调失败");
        }
    }

    private Map<String, Object> handleWechatCallback(String code) {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();

        try {
            // 1. 获取 access_token 和 openid
            String tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
            HttpUrl.Builder urlBuilder = HttpUrl.parse(tokenUrl).newBuilder();
            urlBuilder.addQueryParameter("appid", wechatClientId);
            urlBuilder.addQueryParameter("secret", wechatClientSecret);
            urlBuilder.addQueryParameter("code", code);
            urlBuilder.addQueryParameter("grant_type", "authorization_code");

            Request tokenRequest = new Request.Builder()
                    .url(urlBuilder.build())
                    .get()
                    .addHeader("User-Agent", "NexusGo")
                    .build();

            String tokenResponse = client.newCall(tokenRequest).execute().body().string();
            JSONObject tokenInfo = JSONUtil.parseObj(tokenResponse);

            if (tokenInfo.containsKey("errcode")) {
                throw new RuntimeException("获取微信 access_token 失败: " + tokenInfo.getStr("errmsg"));
            }

            String accessToken = tokenInfo.getStr("access_token");
            String openId = tokenInfo.getStr("openid");

            // 2. 获取用户信息
            String userUrl = "https://api.weixin.qq.com/sns/userinfo";
            HttpUrl.Builder userUrlBuilder = HttpUrl.parse(userUrl).newBuilder();
            userUrlBuilder.addQueryParameter("access_token", accessToken);
            userUrlBuilder.addQueryParameter("openid", openId);
            userUrlBuilder.addQueryParameter("lang", "zh_CN");

            Request userRequest = new Request.Builder()
                    .url(userUrlBuilder.build())
                    .get()
                    .addHeader("User-Agent", "NexusGo")
                    .build();

            String userResponse = client.newCall(userRequest).execute().body().string();
            JSONObject userInfo = JSONUtil.parseObj(userResponse);

            if (userInfo.containsKey("errcode")) {
                throw new RuntimeException("获取微信用户信息失败: " + userInfo.getStr("errmsg"));
            }

            // 构造返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("openId", openId);
            result.put("nickname", userInfo.getStr("nickname"));
            result.put("avatar", userInfo.getStr("headimgurl"));
            result.put("accessToken", accessToken);
            result.put("unionId", userInfo.getStr("unionid"));

            return result;
        } catch (Exception e) {
            log.error("处理微信回调失败，code: {}", code, e);
            throw new RuntimeException("处理微信回调失败");
        }
    }


    // 解析 GitHub access_token
    private String parseAccessToken(String response) {
        try {
            // 尝试解析为 JSON 对象
            JSONObject jsonObj = JSONUtil.parseObj(response);
            if (jsonObj.containsKey("access_token")) {
                return jsonObj.getStr("access_token");
            }
        } catch (Exception e) {
            // 如果不是 JSON 格式，尝试按 URL 参数格式解析
            String[] params = response.split("&");
            for (String param : params) {
                if (param.startsWith("access_token=")) {
                    return param.substring("access_token=".length());
                }
            }
        }
        throw new RuntimeException("无法解析 access_token");
    }


    // 解析 QQ access_token
    private String parseQQAccessToken(String response) {
        // QQ 返回格式: access_token=xxx&expires_in=xxx
        String[] params = response.split("&");
        for (String param : params) {
            if (param.startsWith("access_token=")) {
                return param.substring("access_token=".length());
            }
        }
        throw new RuntimeException("无法解析 QQ access_token");
    }

    // 解析 QQ openid
    private String parseQQOpenId(String response) {
        // QQ 返回格式: callback( {"client_id":"xxx","openid":"xxx"} );
        int start = response.indexOf("{");
        int end = response.lastIndexOf("}");
        if (start != -1 && end != -1) {
            String jsonStr = response.substring(start, end + 1);
            JSONObject obj = JSONUtil.parseObj(jsonStr);
            return obj.getStr("openid");
        }
        throw new RuntimeException("无法解析 QQ openid");
    }

}




